package com.liqi.frame.comm.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.liqi.frame.comm.cache.ShiroRedisCacheManager;
import com.liqi.frame.comm.filter.*;
import com.liqi.frame.comm.properties.MockUserProperties;
import com.liqi.frame.comm.properties.ShiroProperties;
import com.liqi.frame.comm.service.UserService;
import com.liqi.frame.comm.session.MySessionListener;
import com.liqi.frame.comm.session.OnlineSessionDAO;
import com.liqi.frame.comm.session.OnlineSessionFactory;
import com.liqi.frame.comm.token.JWTShiroRealm;
import com.liqi.frame.comm.token.MyRealm;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.Filter;
import java.util.*;

/**
 * Shiro配置
 * Created by liqi on 2019/9/23 0023.
 */
@Configuration
public class ShiroConfig {
//    @Value("${spring.redis.host}")
//    private String redisHost;
//
//    @Value("${spring.redis.port}")
//    private int redisPort;
//
//    @Value("${spring.redis.password}")
//    private String redisPassword;
//
//    @Value("${shiro.session.expire}")
//    private int expire;

    @Autowired
    private ShiroProperties shiroProperties;
    @Autowired
    private MockUserProperties mockUserProperties;
    @Autowired
    private UserService userService;


    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //自定义拦截器
        Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
        filtersMap.put("onlineSession", onlineSessionFilter());
        filtersMap.put("onlineSessionSync", onlineSessionSyncFilter());
        filtersMap.put("login", new LoginFilter(mockUserProperties));
        filtersMap.put("permission", new PermissionFilter());
        filtersMap.put("jwt", new JwtAuthFilter(userService));
        shiroFilterFactoryBean.setFilters(filtersMap);
//        配置安全管理(必须)
        shiroFilterFactoryBean.setSecurityManager(securityManager);
// 设置登录页
        shiroFilterFactoryBean.setLoginUrl("/login");
//        权限不足跳转页面
//        shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
////        登录成功后跳转的欢迎页面
//        shiroFilterFactoryBean.setSuccessUrl("/welcome.do");
////        未认证页面
//        shiroFilterFactoryBean.setUnauthorizedUrl("/403.do");

//        拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
//        filterChainDefinitionMap.put("/webjars/**", "anon");
//        不拦截登录页
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/ajax/login", "anon");
        filterChainDefinitionMap.put("/ajax/login/**", "login");
        filterChainDefinitionMap.put("/main/**", "login");

//        filterChainDefinitionMap.put("/index/**", "login");
        filterChainDefinitionMap.put("/manager/**", "login");
        filterChainDefinitionMap.put("/monitor/**", "login");
        filterChainDefinitionMap.put("/favicon.ico", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/index/nav/**", "login");
        filterChainDefinitionMap.put("/error/**", "anon");
        filterChainDefinitionMap.put("/filterExceptionController/**", "anon");

        filterChainDefinitionMap.put("/comm/**", "anon");

        filterChainDefinitionMap.put("/swagger-ui.html", "login");
        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
        filterChainDefinitionMap.put("/v2/api-docs", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");

        filterChainDefinitionMap.put("/sso/**", "anon");

        filterChainDefinitionMap.put("/actuator/**", "anon");

        filterChainDefinitionMap.put("/websocket/**", "anon");

        filterChainDefinitionMap.put("/test/**", "anon");
        filterChainDefinitionMap.put("/testMQ/**", "anon");
        filterChainDefinitionMap.put("/wechat/**", "anon");
//        提供给最三方的接口
        filterChainDefinitionMap.put("/port/**", "anon");


//        filterChainDefinitionMap.put("/**/ajax/**", "jwt");;
        filterChainDefinitionMap.put("/api/login", "anon");
        filterChainDefinitionMap.put("/**/api/**", "jwt");

        // 配置退出过滤器，其中具体的退出代码 Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        //主要这行代码必须放在所有权限设置的最后，不然会导致所有 url 都被拦截 剩余的都需要认证
//        filterChainDefinitionMap.put("/**", "authc");
        filterChainDefinitionMap.put("/**", "authc,onlineSession,onlineSessionSync");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;

    }

    @Bean
    public OnlineSessionFilter onlineSessionFilter() {
        OnlineSessionFilter onlineSessionFilter = new OnlineSessionFilter();
        return onlineSessionFilter;
    }


    /**
     * 自定义在线用户同步过滤器
     */
    @Bean
    public OnlineSessionSyncFilter onlineSessionSyncFilter() {
        OnlineSessionSyncFilter onlineSessionSyncFilter = new OnlineSessionSyncFilter();
        return onlineSessionSyncFilter;
    }

    /**
     * 安全管理配置
     * @return
     */
    @Bean
//    customRealm应该先被初始化
    @DependsOn({"customRealm"})
    public SecurityManager securityManager(MyRealm customRealm, RedisTemplate<Object, Object> template) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//        注意这里必须配置securityManager
//        SecurityUtils.setSecurityManager(defaultSecurityManager);
        // 配置 rememberMeCookie 查看源码可以知道，这里的rememberMeManager就仅仅是一个赋值，所以先执行
        securityManager.setRememberMeManager(rememberMeManager());
        if (!Objects.equals(shiroProperties.getUseCache(),"off")) {
//        根据情况选择缓存器
            securityManager.setCacheManager(cacheManager(template));
        }
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
//        securityManager.setRealm(customRealm(template));
        securityManager.setRealms(Arrays.asList(customRealm(template), jwtShiroRealm(userService)));

        return securityManager;
    }

    @Bean
//    @DependsOn("lifecycleBeanPostProcessor")
    public MyRealm customRealm(RedisTemplate<Object, Object> template) {
        MyRealm customRealm = new MyRealm();
        if (!Objects.equals(shiroProperties.getUseCache(),"off")) {
////        根据情况使用缓存器
            customRealm.setCacheManager(cacheManager(template));
        }
        return customRealm;
    }

    /**
     * 用于JWT token认证的realm
     */
    @Bean("jwtRealm")
    public Realm jwtShiroRealm(UserService userService) {
        JWTShiroRealm myShiroRealm = new JWTShiroRealm(userService);
        return myShiroRealm;
    }


    /**
     * cacheManager 缓存 redis实现
     * 使用的是shiro-redis开源插件
     * @return
     */
    public ShiroRedisCacheManager cacheManager(RedisTemplate template) {
        return new ShiroRedisCacheManager(template);

//        RedisCacheManager redisCacheManager = new RedisCacheManager();
//        redisCacheManager.setRedisManager(redisManager());
//        redisCacheManager.setPrincipalIdFieldName("id");
//        redisCacheManager.setExpire(expire);//缓存过期时间：秒
//        return redisCacheManager;
    }

    /**
     * rememberMe cookie 效果是重开浏览器后无需重新登录
     * @return
     */
    private SimpleCookie rememberMeCookie() {
        // 这里的Cookie的默认名称是 CookieRememberMeManager.DEFAULT_REMEMBER_ME_COOKIE_NAME
        SimpleCookie cookie = new SimpleCookie(CookieRememberMeManager.DEFAULT_REMEMBER_ME_COOKIE_NAME);
        // 是否只在https情况下传输
        cookie.setSecure(false);
        // 设置 cookie 的过期时间，单位为秒，这里为一天
        cookie.setMaxAge(2592000);
        return cookie;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     */
//    public RedisManager redisManager() {
//        RedisManager redisManager = new RedisManager();
//        redisManager.setHost(redisHost);
//        redisManager.setPort(redisPort);
//        redisManager.setTimeout(30000);//连接redis超时
//        if (!StrUtil.isEmpty(redisPassword))
//            redisManager.setPassword(redisPassword);
//        return redisManager;
//    }

    /**
     * cookie管理对象
     *
     * @return CookieRememberMeManager
     */
    private CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        // rememberMe cookie 加密的密钥
        cookieRememberMeManager.setCipherKey(Base64.decode("ZWvohmPdUsAWT3=KpPqda"));
        return cookieRememberMeManager;
    }

    /**
     * 用于开启 Thymeleaf 中的 shiro 标签的使用
     * @return ShiroDialect shiro 方言对象
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    /**
     * Session Manager
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();

        // 配置自定义的session监听器
        Collection<SessionListener> listeners = new ArrayList<SessionListener>();
        listeners.add(new MySessionListener());
        sessionManager.setSessionListeners(listeners);

        // 设置session超时时间，单位为毫秒
        sessionManager.setGlobalSessionTimeout(shiroProperties.getSessionTimeout());
        sessionManager.setSessionIdCookie(new SimpleCookie(shiroProperties.getSessionIdName()));
        // 网上各种说要自定义sessionDAO 其实完全不必要，shiro自己就自定义了一个，可以直接使用，还有其他的DAO，自行查看源码即可
//        sessionManager.setSessionDAO(enterpriseCacheSessionDAO());
        sessionManager.setSessionDAO(onlineSessionDAO());
        sessionManager.setSessionFactory(sessionFactory());
        return sessionManager;


//        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//        sessionManager.setSessionDAO(redisSessionDAO());
//        return sessionManager;
    }


    @Bean
    public OnlineSessionDAO onlineSessionDAO(){
        OnlineSessionDAO onlineSessionDAO = new OnlineSessionDAO();
        return onlineSessionDAO;
    }

    @Bean
    public EnterpriseCacheSessionDAO enterpriseCacheSessionDAO(){
        EnterpriseCacheSessionDAO enterpriseCacheSessionDAO = new EnterpriseCacheSessionDAO();
        return enterpriseCacheSessionDAO;
    }

    /**
     * 自定义sessionFactory会话
     */
    @Bean
    public OnlineSessionFactory sessionFactory() {
        OnlineSessionFactory sessionFactory = new OnlineSessionFactory();
        return sessionFactory;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
//    @Bean
//    public RedisSessionDAO redisSessionDAO() {
//        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
//        redisSessionDAO.setExpire(expire);//session会话过期时间，默认就是1800秒
//        redisSessionDAO.setRedisManager(redisManager());
//        return redisSessionDAO;
//    }

    /**
     * 开启shiro aop注解支持.
     * 使用代理方式;所以需要开启代码支持;
     * @param securityManager
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * LifecycleBeanPostProcessor，这是个DestructionAwareBeanPostProcessor的子类，
     * 负责org.apache.shiro.util.Initializable类型bean的生命周期的，初始化和销毁。
     * 主要是AuthorizingRealm类的子类，以及EhCacheManager类。
     */
//    @Bean(name = "lifecycleBeanPostProcessor")
//    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
//        return new LifecycleBeanPostProcessor();
//    }

//    @Bean
//    public CacheManager shiroRedisCacheManager() {
//        return new CustomShiroCacheManager();
//    }


}
